package com.autonavi.yunda.yunji.common.web;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lbp.tracelog.utils.Constant;
import com.autonavi.yunda.yunji.common.utils.http.RequestParser;
import com.google.common.collect.Lists;
import com.taobao.eagleeye.EagleEye;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * @author liwenjie
 * @date 2022/09/19
 */
public class TracedHttpSpringFilterConfig implements Filter {
    private static final Logger LOGGER = LoggerFactory.getLogger("AMAP_HTTP_TRACER");
    private String gsidField = null;
    private String bizidField = null;
    private boolean responseLogging = false;
    private boolean requestLogging = false;
    private Set<String> ignoreHeader = new HashSet();
    private Set<String> ignoreCookie = new HashSet();
    private Set<String> ignoreParam = new HashSet();
    private boolean requestParse = false;

    public TracedHttpSpringFilterConfig() {
        this.ignoreHeader.addAll(Arrays.asList(
                "eagleeye-traceid;eagleeye-rpcid;eagleeye-userdata;accept-encoding;accept-language;accept;content-length;connection;upgrade-insecure-requests"
                        .split(";")));
        this.ignoreCookie.addAll(Arrays.asList("jsessionid".split(";")));
    }

    private static List<String> excludes = Lists.newArrayList();

    public void addExcludes(List<String> excludes) {
        TracedHttpSpringFilterConfig.excludes = excludes;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String responseLogging = filterConfig.getInitParameter("responseLogging");
        String requestLogging = filterConfig.getInitParameter("requestLogging");
        String requestParse = filterConfig.getInitParameter("requestParse");
        String gsidField = filterConfig.getInitParameter("gsidField");
        String bizidField = filterConfig.getInitParameter("bizidField");
        String ignoreHeader = filterConfig.getInitParameter("ignoreHeader");
        String ignoreCookie = filterConfig.getInitParameter("ignoreCookie");
        String ignoreParam = filterConfig.getInitParameter("ignoreParam");
        if (responseLogging != null && "true".equals(responseLogging)) {
            this.responseLogging = true;
        }

        if (requestLogging != null && "true".equals(requestLogging)) {
            this.requestLogging = true;
        }

        if (requestParse != null && "true".equals(requestParse)) {
            this.requestParse = true;
        }

        String s;
        if (gsidField != null) {
            s = gsidField.trim();
            if (s.length() > 0) {
                this.gsidField = s;
            }
        }

        if (bizidField != null) {
            s = bizidField.trim();
            if (s.length() > 0) {
                this.bizidField = s;
            }
        }

        String[] params;
        if (ignoreHeader != null) {
            params = ignoreHeader.trim().toLowerCase().split(";");
            this.ignoreHeader.addAll(Arrays.asList(params));
        }

        if (ignoreCookie != null) {
            params = ignoreCookie.trim().toLowerCase().split(";");
            this.ignoreCookie.addAll(Arrays.asList(params));
        }

        if (ignoreParam != null) {
            params = ignoreParam.trim().toLowerCase().split(";");
            this.ignoreParam.addAll(Arrays.asList(params));
        }

    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && !shouldNotFilter((HttpServletRequest) request)) {
            long startMills = System.currentTimeMillis();
            HttpServletRequest httpRequest = this.requestLogging ? new ContentCachingRequestWrapper(
                    (HttpServletRequest) request) : (HttpServletRequest) request;
            HttpServletResponse httpResponse = this.responseLogging ? new ContentCachingResponseWrapper(
                    (HttpServletResponse) response) : (HttpServletResponse) response;
            chain.doFilter((ServletRequest) httpRequest, (ServletResponse) httpResponse);
            String traceId = EagleEye.getTraceId();
            String gsid = null;
            String bizId = null;
            String url = ((HttpServletRequest) httpRequest).getRequestURI();
            JSONObject reqHeader = new JSONObject();
            Enumeration reqHeaderEnum = ((HttpServletRequest) httpRequest).getHeaderNames();

            while (reqHeaderEnum != null && reqHeaderEnum.hasMoreElements()) {
                String p = (String) reqHeaderEnum.nextElement();
                String v = ((HttpServletRequest) httpRequest).getHeader(p);
                if (this.gsidField != null && this.gsidField.equalsIgnoreCase(p)) {
                    gsid = v;
                }

                if (this.bizidField != null && this.bizidField.equalsIgnoreCase(p)) {
                    bizId = v;
                }

                if (!this.ignoreHeader.contains(p.toLowerCase()) && !"cookie".equals(p.toLowerCase())) {
                    reqHeader.put(p, v);
                }
            }
            String method = ((HttpServletRequest) request).getMethod();
            reqHeader.put("method", method);
            reqHeader.put("traceIp", RequestParser.getRemoteIp(httpRequest));

            JSONObject reqCookie = new JSONObject();
            Cookie[] cookies = ((HttpServletRequest) httpRequest).getCookies();
            if (cookies != null) {
                Cookie[] var16 = cookies;
                int var17 = cookies.length;

                for (int var18 = 0; var18 < var17; ++var18) {
                    Cookie cookie = var16[var18];
                    String p = cookie.getName();
                    if (gsid == null && this.gsidField != null && this.gsidField.equalsIgnoreCase(p)) {
                        gsid = cookie.getValue();
                    }

                    if (bizId == null && this.bizidField != null && this.bizidField.equalsIgnoreCase(p)) {
                        bizId = cookie.getValue();
                    }

                    if (!this.ignoreCookie.contains(p.toLowerCase())) {
                        reqCookie.put(p, cookie.getValue());
                    }
                }
            }

            JSONObject reqParam = new JSONObject();
            Enumeration reqParamEnum = ((HttpServletRequest) httpRequest).getParameterNames();

            String reqBody;
            while (reqParamEnum.hasMoreElements()) {
                reqBody = (String) reqParamEnum.nextElement();
                if (bizId == null && this.bizidField != null && this.bizidField.equalsIgnoreCase(reqBody)) {
                    bizId = httpRequest.getParameter(reqBody);
                }

                if (gsid == null && this.gsidField != null && this.gsidField.equalsIgnoreCase(reqBody)) {
                    gsid = httpRequest.getParameter(reqBody);
                }

                if (!this.ignoreParam.contains(reqBody.toLowerCase())) {
                    reqParam.put(reqBody, httpRequest.getParameter(reqBody));
                }
            }

            reqBody = null;
            if (this.requestLogging) {
                reqBody = getRequestData(httpRequest);

                try {
                    if (this.requestParse && !this.ignoreParam.isEmpty()) {
                        String bodyStr = reqBody.trim();
                        if (bodyStr.startsWith("{")) {
                            JSONObject bodyJSON = JSON.parseObject(bodyStr);
                            JSONObject result = new JSONObject();
                            Iterator var22 = bodyJSON.keySet().iterator();

                            while (var22.hasNext()) {
                                String p = (String) var22.next();
                                if (!this.ignoreParam.contains(p.toLowerCase())) {
                                    result.put(p, bodyJSON.get(p));
                                }
                            }

                            reqBody = result.toJSONString();
                        } else if (bodyStr.startsWith("[")) {
                            JSONArray bodyArray = JSON.parseArray(bodyStr);
                            JSONArray result = new JSONArray();

                            for (int i = 0; i < bodyArray.size(); ++i) {
                                JSONObject obj = bodyArray.getJSONObject(i);
                                JSONObject tmp = new JSONObject();
                                Iterator var25 = obj.keySet().iterator();

                                while (var25.hasNext()) {
                                    String p = (String) var25.next();
                                    if (!this.ignoreParam.contains(p.toLowerCase())) {
                                        tmp.put(p, obj.get(p));
                                    }
                                }

                                result.add(tmp);
                            }

                            reqBody = result.toJSONString();
                        }
                    }
                } catch (Exception var27) {
                }
            }

            if (gsid != null) {
                EagleEye.putUserData("gsid", gsid);
            }

            JSONObject rspHeader = new JSONObject();
            Collection<String> rspHeaderCol = httpResponse.getHeaderNames();
            Iterator var39 = rspHeaderCol.iterator();

            while (var39.hasNext()) {
                String header = (String) var39.next();
                if (!this.ignoreHeader.contains(header.toLowerCase())) {
                    rspHeader.put(header, httpResponse.getHeader(header));
                }
            }

            String rspBody = this.responseLogging ? getResponseData(httpResponse) : null;
            LOGGER.info("{}***{}***{}***{}***{}***{}***{}***{}***{}***{}***{}***{}", new Object[]{
                    Constant.APP_NAME == null ? "-" : Constant.APP_NAME,
                    traceId == null ? "-" : traceId.replaceAll("\\*\\*\\*", "**"),
                    gsid == null ? "-" : gsid.replaceAll("\\*\\*\\*", "**"),
                    bizId == null ? "-" : bizId.replaceAll("\\*\\*\\*", "**"), url.replaceAll("\\*\\*\\*", "**"),
                    reqParam.isEmpty() ? "-" : JSON.toJSONString(reqParam).replaceAll("\\*\\*\\*", "**"),
                    reqHeader.isEmpty() ? "-" : JSON.toJSONString(reqHeader).replaceAll("\\*\\*\\*", "**"),
                    reqCookie.isEmpty() ? "-" : JSON.toJSONString(reqCookie).replaceAll("\\*\\*\\*", "**"),
                    reqBody == null ? "-" : reqBody.replaceAll("\\*\\*\\*", "**"),
                    rspHeader.isEmpty() ? "-" : JSON.toJSONString(rspHeader).replaceAll("\\*\\*\\*", "**"),
                    rspBody == null ? "-" : rspBody.replaceAll("\\*\\*\\*", "**"),
                    System.currentTimeMillis() - startMills});
        } else {
            chain.doFilter(request, response);
        }
    }

    /**
     * 是否在白名单中，不过滤的uri
     *
     * @return
     */
    private boolean shouldNotFilter(HttpServletRequest request) {
        return excludes.contains(request.getRequestURI());
    }

    private static String getRequestData(HttpServletRequest request) {
        String payload = null;

        try {
            ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request,
                    ContentCachingRequestWrapper.class);
            if (wrapper != null) {
                byte[] buf = wrapper.getContentAsByteArray();
                if (buf.length > 0) {
                    payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
                }
            }
        } catch (Exception var4) {
        }

        return payload;
    }

    private static String getResponseData(HttpServletResponse response) throws IOException {
        String payload = null;
        ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response,
                ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            byte[] buf = wrapper.getContentAsByteArray();
            if (buf.length > 0) {
                payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
                wrapper.copyBodyToResponse();
            }
        }

        return payload;
    }

}
